跳到主要内容

TS Equals

· 阅读需 4 分钟

在做 ts 类型练习的时候,碰到需要比较两个类型是否相同,看到答案里有这么一种方法

export type Equals<X, Y> =
(<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? true : false;

这里的泛型 T 是什么意思?这段代码该怎么理解?

搜索到比较好的答案在这里,也顺便记录下自己看完后的理解

泛型 T 是什么意思?

没有意思!!这就是一个定义的泛型,只是我们平时用的比较多的是像这样

type Identity<T> = (data: T) => T

区别只是上面这段代码在执行时会自动根据传入的 data 类型推断 T 的类型,而 Equal 中的写法需要手动传入,默认是 unknown,没有任何特殊的含义

怎么理解这段代码

Equal 这段代码重点在于比较两个函数 (<T>() => T extends X ? 1 : 2)(<T>() => T extends Y ? 1 : 2) 是否相同。

那么问题就变成 X 和 Y 是否相同。

如果 X 和 Y 相同,没什么好说的, extends 一定成立;如果不同时,那么我们总能找到一种类型,使得用它作为 T 时,extends 不成立。代码如下

declare let x: <T>() => T extends number ? 1 : 2
declare let y: <T>() => T extends string ? 1 : 2

const a = x<string>() // 2
const b = x<number>() // 1

const c = y<string>() // 1
const d = y<number>() // 2
declare let x: <T>() => T extends { foo: string; bar: number } ? 1 : 2
declare let y: <T>() => T extends { foo: string } ? 1 : 2

const a = x<{ foo: string }>() // 2
const b = y<{ foo: string }>() // 1

因此我们就可以用它来判断 X 和 Y 是否相同。

特殊情况

以上只是解释了 Equal 的基本原理,ts 内部实现可能要复杂的多,可能有时候为了性能或者什么原因,会出现一些和常规认识不一样的结果

NativeEqual

type NaiveEquals<X, Y> = X extends Y ? (Y extends X ? true : false) : false

type A = NaiveEquals<{ a?: number }, {}> // true
type B = Equals<{ a?: number }, {}> // false

理论上 A 也应该是 false,因为类型 {a: string} 可分配给 ,但不能分配给 {a?: number}

联合类型

type A = Equals<{ x: 1 } & { y: 2 }, { x: 1; y: 2 }> // false

理论上这里 A 应该是 true,但结果却是 false,了解不了,但是事实。

总结

Equal 当作日常使用应该是没问题的,特殊情况我觉得完全可以当作补充知识去了解下。

再次说明,原文在这里